package com.mebs.model.logistics;

import com.mebs.model.DimensionUnit;
import com.mebs.model.WeightUnit;
import com.mebs.model.AuditInfo;
import com.mebs.model.VolumeUnit;
import com.mebs.web.MEBSUtils;

import javax.persistence.*;

import org.jt.jet.annotation.Field;
import org.jt.jet.annotation.ArgumentType;
import static org.hibernate.proxy.HibernateProxyHelper.getClassWithoutInitializingProxy;

import java.util.List;
import java.util.LinkedList;
import java.text.NumberFormat;

/**
 * Created by Sergey Prasolov, sap@jensentechnologies.com Date: Apr 7, 2006, 1:56:56 PM
 */
@Entity
@Inheritance(strategy = InheritanceType.JOINED)
@Table(name = "BOXES")
public abstract class Box {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "Id")
    private Long id;

    @Column(name = "Number", nullable = false)
    private Integer number;

    @Column(name = "DimL", nullable = true)
    private Double dimL;

    @Column(name = "DimW", nullable = true)
    private Double dimW;

    @Column(name = "DimH", nullable = true)
    private Double dimH;

    @Column(name = "DimUnit", nullable = true)
    private Integer dimUnit;

//    @Column(name = "Volume", nullable = true)
//    private Double volume;
//
//    @Column(name = "volumeUnit", nullable = true)
//    private Integer volumeUnit;

    @Column(name = "Weight", nullable = true)
    private Double weight;

    @Column(name = "WeightUnit", nullable = true)
    private Integer weightUnit;

    @ManyToOne(optional = true, fetch = FetchType.LAZY)
    @JoinColumn(name = "AirId")
    @Field(type = ArgumentType.INSTANCE)
    private Air air;

    @ManyToOne(optional = true, fetch = FetchType.LAZY)
    @JoinColumn(name = "OceanContainerId")
    @Field(type = ArgumentType.INSTANCE)
    private OceanContainer oceanContainer;

    @ManyToOne(optional = true, fetch = FetchType.LAZY)
    @JoinColumn(name = "TruckId")
    @Field(type = ArgumentType.INSTANCE)
    private Truck truck;

    @ManyToOne(optional = true, fetch = FetchType.LAZY)
    @JoinColumn(name = "LotId")
    @Field(type = ArgumentType.INSTANCE)
    private Lot lot;

    @ManyToOne(optional = true, fetch = FetchType.LAZY)
    @JoinColumn(name = "OverpackedBoxId")
    @Field(type = ArgumentType.INSTANCE)
    private OverpackedBox overpackedBox;

	@Column(name = "IsPallet", nullable = true)
	private Boolean isPallet;

	@Embedded
    private AuditInfo auditInfo;

    /**
     * Constructors
     */

    public Box()
    {
    }

    public Box(
        Integer number,
        Double dimL,
        Double dimW,
        Double dimH,
        Integer dimUnit,
        //        Double volume,
        //        Integer volumeUnit,
        Double weight,
        Integer weightUnit)
    {
        this.number = number;
        this.dimL = dimL;
        this.dimW = dimW;
        this.dimH = dimH;
        this.dimUnit = dimUnit;
//        this.volume = volume;
//        this.volumeUnit = volumeUnit;
        this.weight = weight;
        this.weightUnit = weightUnit;
    }

    /**
     * ********************** Getters-setters ***********************
     */

    public Long getId()
    {
        return id;
    }

    public void setId(Long id)
    {
        this.id = id;
    }

	public Integer getNumber() {
		return number;
	}

	public void setNumber(Integer number) {
		this.number = number;
	}

	public Double getDimL()
    {
        return dimL;
    }

    public void setDimL(Double dimL)
    {
        this.dimL = dimL;
    }

    public Double getDimW()
    {
        return dimW;
    }

    public void setDimW(Double dimW)
    {
        this.dimW = dimW;
    }

    public Double getDimH()
    {
        return dimH;
    }

    public void setDimH(Double dimH)
    {
        this.dimH = dimH;
    }

    public Integer getDimUnit()
    {
        return dimUnit;
    }

    public void setDimUnit(Integer dimUnit)
    {
        this.dimUnit = dimUnit;
    }

    @Transient
    public Double getVolume()
    {
        if (dimL != null &&
            dimW != null &&
            dimH != null) {

            NumberFormat nf = NumberFormat.getInstance();
            nf.setMaximumFractionDigits(2);
            nf.setMinimumFractionDigits(2);
			Double d = dimL * dimW * dimH * Math.pow(DimensionUnit.getObjectByType(dimUnit).getCoef(), 3);
			String str = nf.format(d).replaceAll("\\D", "");
			return Double.parseDouble(str);
        }

        return null;
    }

//    public void setVolume(Double volume)
//    {
//        this.volume = volume;
//    }
//
//    public Integer getVolumeUnit()
//    {
//        return volumeUnit;
//    }
//
//    public void setVolumeUnit(Integer volumeUnit)
//    {
//        this.volumeUnit = volumeUnit;
//    }

    public Double getWeight()
    {
        return weight;
    }

    public void setWeight(Double weight)
    {
        this.weight = weight;
    }

    public Integer getWeightUnit()
    {
        return weightUnit;
    }

    public void setWeightUnit(Integer weightUnit)
    {
        this.weightUnit = weightUnit;
    }

    public Air getAir()
    {
        return air;
    }

    public void setAir(Air air)
    {
        this.air = air;
    }

    public OceanContainer getOceanContainer()
    {
        return oceanContainer;
    }

    public void setOceanContainer(OceanContainer oceanContainer)
    {
        this.oceanContainer = oceanContainer;
    }

    public Truck getTruck()
    {
        return truck;
    }

    public void setTruck(Truck truck)
    {
        this.truck = truck;
    }

    public Lot getLot()
    {
        return lot;
    }

    public void setLot(Lot lot)
    {
        this.lot = lot;
    }

    public OverpackedBox getOverpackedBox()
    {
        return overpackedBox;
    }

    public void setOverpackedBox(OverpackedBox overpackedBox)
    {
        this.overpackedBox = overpackedBox;
    }

    public AuditInfo getAuditInfo()
    {
        if (this.auditInfo == null) {
            this.auditInfo = new AuditInfo();
        }
        return auditInfo;
    }

    public void setAuditInfo(AuditInfo auditInfo)
    {
        this.auditInfo = auditInfo;
    }

	public Boolean getPallet() {
		return isPallet;
	}

	public void setPallet(Boolean pallet) {
		isPallet = pallet;
	}

	@Override
    public String toString()
    {
        return this.getNumber() == null ? "" : this.getNumber().toString();
    }
	
	@Transient
    public String getShortDimUnitString()
    {
        return dimUnit != null ? DimensionUnit.getObjectByType(dimUnit).getShortName() : "";
    }

    @Transient
    public String getDimUnitString()
    {
        return dimUnit != null ? DimensionUnit.getObjectByType(dimUnit).getName() : "";
    }

    @Transient
    public String getVolumeUnitString()
    {
//        return volumeUnit != null ? VolumeUnit.getObjectByType(volumeUnit).getName() : "";
        return VolumeUnit.CBM.getName();
    }
    
    @Transient
    public String getShortWeightUnitString()
    {
        return weightUnit != null ? WeightUnit.getObjectByType(weightUnit).getShortName() : "";
    }

    @Transient
    public String getWeightUnitString()
    {
        return weightUnit != null ? WeightUnit.getObjectByType(weightUnit).getName() : "";
    }

    @Transient
    public boolean isPacked()
    {
        return (this.overpackedBox != null);
    }

    @Transient
    public boolean isPackingOf(Box box)
    {
        if (box == null) return false;
        if (box.getOverpackedBox() == null) return false;
        else if (this.equals(box.getOverpackedBox())) return true;
        return isPackingOf(box.getOverpackedBox());
    }

    @Transient
    public List<Box> getPackWay()
    {
        List<Box> packWay = new LinkedList<Box>();

        if (this.overpackedBox != null) {
            packWay.add(this.overpackedBox);
            packWay.addAll(this.overpackedBox.getPackWay());
        }

        return packWay;
    }

    @Transient
    public abstract boolean isDeletable();

    @Transient
    public Double getChargeableWeight()
    {
        if ((dimL == null) || (dimW == null) || (dimH == null) || (dimUnit == null)) {
            return 0d;
        } else if ((dimL == 0) || (dimW == 0) || (dimH == 0)) {
            return 0d;
        }
        try {
            DimensionUnit unit = DimensionUnit.getObjectByType(dimUnit);
            double sum = dimL * dimH * dimW / 166.0;
            if (unit != DimensionUnit.IN) sum *= Math.pow((unit.getCoef() / DimensionUnit.IN.getCoef()), 3);
            return MEBSUtils.dot2f(sum);
        } catch (RuntimeException re) {
            return 0d;
        }
    }

    @Override
    public int hashCode()
    {
        if (getId() == null) return super.hashCode();
        return getId().hashCode();
    }

    @Override
    public boolean equals(Object o)
    {
        if (this == o) return true;
        if (getId() == null) return false;
        if (o == null) return false;
        if (getClassWithoutInitializingProxy(o) != getClassWithoutInitializingProxy(this) &&
            ! getClassWithoutInitializingProxy(o).isAssignableFrom(Box.class))
            return false;
        final Box that = (Box) o;
        return getId().equals(that.getId());
    }

    @Transient
    public double getCbmVolume()
    {
        /*if ((volume != null) && (volumeUnit != null)) {
            VolumeUnit vUnit = VolumeUnit.getObjectByType(volumeUnit);
            if (vUnit != null) return volume * vUnit.getCoef();

        } else*/ if ((dimH != null) && (dimL != null) && (dimW != null) && (dimUnit != null)) {
            DimensionUnit dUnit = DimensionUnit.getObjectByType(dimUnit);
            if (dUnit != null) return dimH * dimL * dimW * Math.pow(dUnit.getCoef(), 3);
        }

        return Double.MAX_VALUE;
    }
}
